package org.rasterfun.language2


import org.rasterfun.language2._
import org.rasterfun.language2.num._
import org.rasterfun.language2.bool._
import scala.util.parsing.combinator.syntactical.StdTokenParsers
import util.parsing.combinator.{PackratParsers, ImplicitConversions}

/**
 * A parser for rasterfun language.
 *
 * @author Hans Haggstrom
 */
object LanguageParser extends StdTokenParsers with PackratParsers {

  def parse(text : String): ParseResult[Expression] = phrase(expression)( new lexical.Scanner(text))

  // Setup lexer
  type Tokens = LanguageLexer
  val lexical = new LanguageLexer


  // Parser
	lazy val definition: PackratParser[FuncDef] =
		"def" ~ ident ~ "(" ~ rep(parameterDef) ~")" ~ rep(statement) ~ "return" ~ expression ^^
		{case keyword ~ name ~ lp ~ params ~ rp ~ statements ~ ret ~ returnvalue => FuncDef(name, params, statements, returnvalue)}

	lazy val parameterDef: PackratParser[Parameter] = kind ~ ident ~ opt("=" ~> constantValue) ^^ {case t ~ name ~ value => Parameter(name, t, value)}
	lazy val kind: PackratParser[Kind] = "num" ^^^ NumKind  | "bool" ^^^ BoolKind

  lazy val expression: PackratParser[Expression] = boolExpr | numExpr
	lazy val statement: PackratParser[Statement] = valueDef

	lazy val valueDef: PackratParser[ValueDef] = ident ~ "=" ~ expression ^^ {case i ~ eq ~ v => ValueDef(i, v)}

	lazy val constantValue: PackratParser[Expression] = booleanConst | number

  lazy val boolExpr: PackratParser[Bool] = logic

  /*
  def parameter : Parser[Param] = namedParameter | unnamedParameter | failure("parameter expected")
  def namedParameter : Parser[Param] = ident ~ "=" ~! expression ^^ {case name ~ eq ~ value => Param(name, value)}
  def unnamedParameter : Parser[Param] = expression ^^ {case value => new Param(value)}
  lazy val logic: PackratParser[Bool] =
    boolExpr ~ "and" ~ comparison ^^ {case a ~ op ~ b => And(a, b)} |
    boolExpr ~ "or" ~ comparison ^^ {case a ~ op ~ b => Or(a, b)} |
    boolExpr ~ "xor" ~ comparison ^^ {case a ~ op ~ b => Xor(a, b)} |
    boolExpr ~ "==" ~ comparison ^^ {case a ~ op ~ b => BoolEqual(a, b)} |
    boolExpr ~ "!=" ~ comparison ^^ {case a ~ op ~ b => BoolNotEqual(a, b)} |
    comparison

  lazy val comparison: PackratParser[Bool] =
    numExpr ~ "<" ~ numExpr ^^ {case a ~ op ~ b => Less(a, b)} |
    numExpr ~ ">" ~ numExpr ^^ {case a ~ op ~ b => Greater(a, b)} |
    numExpr ~ "<=" ~ numExpr ^^ {case a ~ op ~ b => LessOrEqual(a, b)} |
    numExpr ~ ">=" ~ numExpr ^^ {case a ~ op ~ b => GreaterOrEqual(a, b)} |
    numExpr ~ "==" ~ numExpr ^^ {case a ~ op ~ b => Equal(a, b)} |
    numExpr ~ "!=" ~ numExpr ^^ {case a ~ op ~ b => NotEqual(a, b)} |
    unaryBoolean

  lazy val unaryBoolean: PackratParser[Bool] =
    "not" ~! atomicBoolean ^^ {case op ~ a => Not(a)} |
    atomicBoolean

  lazy val atomicBoolean: PackratParser[Bool] =
    booleanConst |
    boolParenthesis /*|
    boolIf */

  /*
  lazy val boolIf: PackratParser[Bool] =
    "if" ~ boolExpr ~ ":" ~ boolExpr ~ "else" ~ boolExpr ^^ {case op1 ~ exp ~ op2 ~ th ~ op3 ~ els => BoolIf(exp, th, els)}
  */

  lazy val booleanConst: PackratParser[Bool] =
    "true" ^^^ {True} |
    "false" ^^^ {False}
 */

  lazy val boolParenthesis: PackratParser[Bool] = "(" ~ boolExpr ~ ")" ^^ {case lp ~ a ~ rp => BoolParen(a)}


  
  lazy val numExpr: PackratParser[Num] = terms

  lazy val terms: PackratParser[Num] =
    terms ~ "+" ~ factors ^^ {case a ~ op ~ b => Add(a, b)} |
    terms ~ "-" ~ factors ^^ {case a ~ op ~ b => Sub(a, b)} |
    factors

  lazy val factors: PackratParser[Num] =
    factors ~ "*" ~ unaryNum ^^ {case a ~ op ~ b => Mul(a, b)} |
    factors ~ "/" ~ unaryNum ^^ {case a ~ op ~ b => Div(a, b)} |
    unaryNum

  lazy val unaryNum: PackratParser[Num] =
    "-" ~ atomicNum ^^ {case op ~ a => Neg(a)} |
    atomicNum

  lazy val atomicNum: PackratParser[Num] =
    numParenthesis |
    number |
    numIf

  private def binaryExpression( part : Parser[Expression], op : Parser[String], resultFunc : (Expression, String, Expression) => Expression  ) : Parser[Expression] =
    part ~ rep( op ~! part ) ^^ { case first ~ rest =>
      rest.foldLeft( first ) {
        (ready, next) => next match { case o ~ e => resultFunc( ready, o, e ) }
      }
    }
  lazy val numIf: PackratParser[Num] =
    "if" ~ boolExpr ~ ":" ~ numExpr ~ "else" ~ numExpr ^^ {case op1 ~ exp ~ op2 ~ th ~ op3 ~ els => NumIf(exp, th, els)}
  lazy val numParenthesis: PackratParser[Num] = "(" ~ numExpr ~ ")" ^^ {case lp ~ a ~ rp => NumParen(a)}
  lazy val number: PackratParser[Num] = numericLit ^^ {s => NumConstant(s.toFloat)}

  private def numToNumExpression( part : Parser[Expression], op : Parser[String], resultFunc : (Expression, String, Expression) => Expression  ) : Parser[Expression] =
    part ~ rep( op ~! part ) ^^ { case first ~ rest =>
      rest.foldLeft( first ) {
        (ready, next) => next match { case o ~ e => ready.operation(o, e)}
      }
    }
/*
  def unary: Parser[Expression] =
    "not" ~! extend ^^ {case op ~ a => Not(a)} |
            "-" ~! extend ^^ {case op ~ a => Neg(a)} |
            extend
  def extend: Parser[Expression] =
    invoke ~ "&" ~! repsep(invoke, "&") ^^ {case first ~ op ~ rest => rest.foldLeft(first) {(leftSide, next) => Extend(leftSide, next)}} |
            invoke

  def invoke: Parser[Expression] =
    (
            primitive ~ rep(
              ("(" ~! repsep(parameter, opt(",")) <~ ")" ^^ {case lp ~ params => {leftSide: Expression => Call(leftSide, params)}}) |
                      ("." ~! ident ^^ {case dt ~ id => {leftSide: Expression => Dot(leftSide, id)}})
              )
            ) ^^ {case first ~ rest => rest.foldLeft(first) {(leftSide, next) => next(leftSide)}}

  def primitive: Parser[Expression] = boolean | number | text | block | list | parens | reference | failure("Expression expected")
*/
  /*
    def parameter : Parser[Param] = namedParameter | unnamedParameter | failure("parameter expected")
    def namedParameter : Parser[Param] = ident ~ "=" ~! expression ^^ {case name ~ eq ~ value => Param(name, value)}
    def unnamedParameter : Parser[Param] = expression ^^ {case value => new Param(value)}

    def reference: Parser[Ref] = ident ^^ { case r => Ref( r ) }

    def definition : Parser[Definition] = rep(metadata) ~ ident ~ ("?" | ":") ~! expression ^^
            { case md ~ name ~ definitionType ~ value => Definition( name, value, makeMetadataMap( md ), definitionType.equals("?")) }
    def metadata : Parser[Metadata] = "@" ~> ident ~ opt( "=" ~! expression  ) ^^
            { case name ~ expr => expr match {
                case Some( c ~ x ) => Metadata(name, x)
                case None => Metadata(name, True) }
            }  |
            "@" ~> err( "metadata expected" )
    private def makeMetadataMap( metadata : List[Metadata] ) = metadata.foldLeft(Map[String, Metadata]()) { (m, d) => m(d.identifier) = d }

    def block : Parser[Block] = "{" ~! importClause ~ repsep( definition, opt(",") ) ~ opt(",") ~ blockResult <~ "}" ^^
            { case lb ~ imports ~ props ~ comma ~ result => Block( imports, props ::: result) } |
            failure("block expected")

    def importClause : Parser[Imports] = rep( "import" ~> rep1sep( importPath, "," )) ^^ { case listOfImports  => Imports( List.flatten( listOfImports ) ) }
    def importPath : Parser[Import] = rep1sep( ident, ".")  ^^ { case path  => Import(path) }

    def blockResult : Parser[List[Definition]] = opt(expression) ^^
            { x => x match { case Some(e) => List( Definition( "result", e, Map.empty, false ) ) ; case None => Nil }}

    def parens : Parser[Expression] =  "(" ~! expression ~ ")" ^^ { case lp ~ e ~ rp => Parens(e) }


    def text : Parser[Text] =  stringLit ^^ { s => Text(s.toString) }

    def list : Parser[Li] = "[" ~! repsep( expression, opt(",") ) <~ opt(",") <~ "]" ^^ { case lb ~ l  => Li(l) }
  */


  /*
    private def binaryExpression( part : Parser[Expression], op : Parser[String], resultFunc : (Expression, String, Expression) => Expression  ) : Parser[Expression] =
      part ~ rep( op ~! part ) ^^ { case first ~ rest =>
        rest.foldLeft( first ) { (ready, next) => next match { case o ~ e => resultFunc( ready, o, e ) }  } }
  */
  /*private def binaryExpression(part: Parser[Expression], ops: BinaryOp*): Parser[Expression] = {

    val operatorParser: Parser[Expression]
    ops foreach {op: BinaryOp =>}

    part ~ rep(op ~! part) ^^ {
      case first ~ rest =>
        rest.foldLeft(first) {(ready, next) => next match {case o ~ e => resultFunc(ready, o, e)}}
    }
  }
*/

}
